/*
 * This file is part of Iqm.
 * Copyright (c) 2010-2011 Helmut Ahammer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package opgui.preferences;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.jai.ParameterBlockJAI;
import javax.xml.bind.JAXBException;
import opgui.preferences.jaixml.*;
import opgui.preferences.jaixml.JaiOperationDescriptor.*;
import opgui.preferences.jaixml.JaiPreferences.*;

/**
 * This class is used to load and set GUI preferences and JAI-parameter-blocks.
 * @author Philipp W. <java@scaenicus.net>
 * @version 0.0.1
 */
public final class JAIXMLParameterBlocks {

    private JaiPreferences propertyXML;
//    private FileWriter xmlWriter;

    /**
     * Constructs a new JAIXMLParameterBlocks and a new jaitemplates.xml if necessary.
     */
    public JAIXMLParameterBlocks() {
        String pathName = System.getProperty("user.home") + System.getProperty("file.separator") + "iqm" + System.getProperty("file.separator");
        String fileName = "jaitemplates.xml";
        File file = new File(pathName);
        file.mkdirs();
        File xmlFile = new File(pathName + fileName);
        if (xmlFile.exists()) {
            this.readXML(xmlFile);
        } else {
            getXMLDoc();
        }
        file = null;
        xmlFile = null;
    }

    /**
     * Returns an object of the DOM-representation of the XML-file currently written.
     * It it called by the constructor.
     * @return Object representing the XML.
     */
    private JaiPreferences getXMLDoc() {
        if (propertyXML == null) {
            propertyXML = new ObjectFactory().createJaiPreferences();
        }
        return propertyXML;
    }

    /**
     * Writes the jaitemplates.xml to the "iqm"-user-folder of the application.
     * This function is <b>synchronized</b>.
     */
    public synchronized void writeXML() {
        String pathName = System.getProperty("user.home") + System.getProperty("file.separator") + "iqm" + System.getProperty("file.separator");
//        Calendar cal = Calendar.getInstance();
//        cal.setTime(new java.util.Date());
//        /*Time-Format:
//        'F'  	ISO 8601  complete date formatted as "%tY-%tm-%td".
//        'T'  	Time formatted for the 24-hour clock as "%tH:%tM:%tS".
//        'H'  	Hour of the day for the 24-hour clock, formatted as two digits with a leading zero as necessary i.e. 00 - 23.
//        'M' 	Minute within the hour formatted as two digits with a leading zero as necessary, i.e. 00 - 59.
//        'S' 	Seconds within the minute, formatted as two digits with a leading zero as necessary, i.e. 00 - 60 ("60" is a special value required to support leap seconds).
//        'L'  	Millisecond within the second formatted as three digits with leading zeros as necessary, i.e. 000 - 999.
//        'N' 	Nanosecond within the second, formatted as nine digits with leading zeros as necessary, i.e. 000000000 - 999999999.
//         */
//        String fileName = String.format("%1$tF_%1$tH-%1$tM-%1$tS", cal) + ".xml";

        String fileName = "jaitemplates.xml";
        File xmlFile = new File(pathName + fileName);
        FileWriter fw;
        try {
            fw = new FileWriter(xmlFile);
            /*/fw.write("<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" + "\n" +
            "<?xml-stylesheet type='text/xsl' href='jaipreferences.xsl' ?>" + "\n");*/
            try {
                javax.xml.bind.JAXBContext jaxbCtx = javax.xml.bind.JAXBContext.newInstance(getXMLDoc().getClass().getPackage().getName());

                javax.xml.bind.Marshaller marshaller = jaxbCtx.createMarshaller();
                marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8"); //NOI18N
                marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                marshaller.marshal(getXMLDoc(), fw);
            } catch (JAXBException ex) {
                Logger.getLogger(JAIXMLParameterBlocks.class.getName()).log(Level.SEVERE, null, ex);
            }
            fw.close();
        } catch (IOException ex) {
            Logger.getLogger(JAIXMLParameterBlocks.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Reads the specified XML-file.
     * This function is <b>synchronized</b>.
     * @param xmlFile XML-file to read in.
     * @see ImgProcessingVault
     */
    public synchronized void readXML(File xmlFile) {
        try {
            javax.xml.bind.JAXBContext jaxbCtx;
            jaxbCtx = javax.xml.bind.JAXBContext.newInstance(JaiPreferences.class.getPackage().getName());
            javax.xml.bind.Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();
            propertyXML = (JaiPreferences) unmarshaller.unmarshal(xmlFile); //NOI18N
        } catch (JAXBException ex) {
            Logger.getLogger(JAIXMLParameterBlocks.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Returns a List of JaiOperationDescriptor-template from the XML by the given name.
     * @param parameterblockname ParameterBlock-name (GUI-name) to find in the jaitemplates.xml
     * @return List<jaixml.JaiOperationDescriptor> or NULL if no element was found.
     */
    public ParameterBlock getParameterBlockByName(String parameterblockname) {
        List<ParameterBlock> parameterblocks = propertyXML.getParameterBlock();
        for (ParameterBlock pb : parameterblocks) {
            if (parameterblockname.equalsIgnoreCase(pb.getBlockname())) {
                return pb;
            }
        }
        return null;
        //throw new Exception(JAIXMLParameterBlocks.class.getName() + " :: getByName :: " + "No such element!");
    }

    /**
     * Returns a JaiOperationDescriptor from the XML by the given name.
     * @param parameterblockname ParameterBlock-name (GUI-name) to find in the jaitemplates.xml
     * @param templatename Template-name to find in the jaitemplates.xml
     * @return jaixml.JaiOperationDescriptor or NULL if no element was found.
     */
    public JaiOperationDescriptor getTemplateByName(String parameterblockname, String templatename) {
        List<ParameterBlock> parameterblocks = propertyXML.getParameterBlock();
        for (ParameterBlock pb : parameterblocks) {
            if (parameterblockname.equalsIgnoreCase(pb.getBlockname())) {
                for (JaiOperationDescriptor tlt : pb.getTemplate()) {
                    if (tlt.getTemplatename().equalsIgnoreCase(templatename)) {
                        return tlt;
                    }
                }
            }
        }
        return null;
        //throw new Exception(JAIXMLParameterBlocks.class.getName() + " :: getByName :: " + "No such element!");
    }

    /**
     * Deletes a template from jaitemplates.xml.
     * @param parameterblockname ParameterBlock-name (GUI-name) of the template to be deleted.
     * @param templatename Template-name to be deleted.
     */
    public void deleteTemplateByName(String parameterblockname, String templatename) {
        List<ParameterBlock> parameterblocks = propertyXML.getParameterBlock();
        for (ParameterBlock pb : parameterblocks) {
            if (parameterblockname.equalsIgnoreCase(pb.getBlockname())) {
                for (JaiOperationDescriptor tlt : pb.getTemplate()) {
                    if (tlt.getTemplatename().equalsIgnoreCase(templatename)) {
                        pb.getTemplate().remove(tlt);
                        writeXML();
                        return;
                    }
                }
            }
        }
    }

    /**
     * Changes values in a ParameterBlockJAI to the settings in the jaitemplates.xml. <br />
     * Currently supported parameter-types: Integer, Boolean, Character, Double, Float.
     * @param block ParameterBlockJAI used as basis for the setting (Use the hard-coded default ParameterBlock if unsure).
     * @param template JaiOperationDescriptor template to fit into the ParameterBlockJAI.
     * @return ParameterBlockJAI with the changes from the jaitemplates.xml.
     * @see JAIXMLParameterBlocks#getTemplateByName(java.lang.String, java.lang.String) For a convenient way to get the correct JaiOperationDescriptor for parameter template.
     */
    public ParameterBlockJAI loadParameterBlockFromTemplate(ParameterBlockJAI block, JaiOperationDescriptor template) {
        List<Parameters> parameters = template.getParameters();
        for (Parameters param : parameters) {
            if (param.getParameterclass().equals(Integer.class.getName())) {
                block.setParameter(param.getParametername(), Integer.valueOf(param.getParametervalue()));
            } else if (param.getParameterclass().equals(Boolean.class.getName())) {
                block.setParameter(param.getParametername(), Boolean.valueOf(param.getParametervalue()));
            } else if (param.getParameterclass().equals(Character.class.getName())) {
                block.setParameter(param.getParametername(), param.getParametervalue().charAt(0));
            } else if (param.getParameterclass().equals(Double.class.getName())) {
                block.setParameter(param.getParametername(), Double.valueOf(param.getParametervalue()));
            } else if (param.getParameterclass().equals(Float.class.getName())) {
                block.setParameter(param.getParametername(), Float.valueOf(param.getParametervalue()));
            }
        }
        return block;
    }

    /**
     * Sets a new template in jaitemplates.xml with the values of a ParameterBlockJAI.<br />
     * Currently supported paramter-types: Integer, Boolean, Character, Double, Float.<br />
     * This function is <b>synchronized</b>.
     * @param parameterblockname ParameterBlock-name (GUI-name) of the new entry to jaitemplates.xml.
     * @param templatename Template-name of the new entry to jaitemplates.xml.
     * @param block Values of this ParameterBlockJAI are saved to jaitemplates.xml.
     */
    public synchronized void setTemplate(String parameterblockname, String templatename, ParameterBlockJAI block) {
        List<Parameters> parameters = null;
        JaiOperationDescriptor template = getTemplateByName(parameterblockname, templatename);
        if (template != null) {
            parameters = template.getParameters();
        }
        if (parameters == null) {
            ParameterBlock paramBlock = getParameterBlockByName(parameterblockname);
            if (paramBlock == null) {
                paramBlock = new ParameterBlock(); //New Parameter-Block
                paramBlock.setBlockname(parameterblockname);
                propertyXML.getParameterBlock().add(paramBlock);
            }
            template = new JaiOperationDescriptor(); //New Template
            template.setTemplatename(templatename);
            paramBlock.getTemplate().add(template);
            parameters = template.getParameters();
        }

        Class[] classes = block.getParamClasses();
        parameters.clear();
        for (String pn : block.getParameterListDescriptor().getParamNames()) {
            String className = classes[block.indexOfParam(pn)].getName();
            System.out.println("Write to jaitemplates.xml " + pn + " :: " + className + " @ " + parameterblockname + "." + templatename);
            Parameters newparam = new Parameters();
            newparam.setParametername(pn);
            newparam.setParameterclass(className);
            if (className.equals(Integer.class.getName())) {
                newparam.setParametervalue(Integer.toString(block.getIntParameter(pn)));
            } else if (className.equals(Boolean.class.getName())) {
                newparam.setParametervalue(Boolean.toString(block.getBooleanParameter(pn)));
            } else if (className.equals(Character.class.getName())) {
                newparam.setParametervalue(Character.toString(block.getCharParameter(pn)));
            } else if (className.equals(Double.class.getName())) {
                newparam.setParametervalue(Double.toString(block.getDoubleParameter(pn)));
            } else if (className.equals(Float.class.getName())) {
                newparam.setParametervalue(Float.toString(block.getFloatParameter(pn)));
            }
            parameters.add(newparam);
            newparam = null;
        }
        writeXML();
    }
}
